package com.zhaogang.transactionManagerDemo.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhaogang.transactionManagerDemo.entity.Account;
import com.zhaogang.transactionManagerDemo.entity.SystemLog;
import com.zhaogang.transactionManagerDemo.ex.DbException;
import com.zhaogang.transactionManagerDemo.mapper.AccountMapper;
import com.zhaogang.transactionManagerDemo.mapper.SystemLogMapper;
import com.zhaogang.transactionManagerDemo.service.AccountTransactionService;
import com.zhaogang.transactionManagerDemo.service.SystemLogService;

@Service("AccountTransactionServiceImpl")
public class AccountTransactionServiceImpl implements AccountTransactionService {
	
	private static ObjectMapper objectMapper = new ObjectMapper();
	private static Logger logger = LoggerFactory.getLogger(SystemLogServiceImpl.class);
	
	@Autowired
	private SystemLogMapper logMapper;	
	
	@Autowired	//@Autowired可以放在属性或者setter方法上
	public void setAccountMapper(AccountMapper accountMapper) {
		this.accountMapper = accountMapper;
	}
	private AccountMapper accountMapper;
	
	public AccountMapper getAccountMapper() {
		return accountMapper;
	}
	
	@Autowired
	public void setLogService(SystemLogService logService) {
		this.logService = logService;
	}
	private SystemLogService logService;
	public SystemLogService getLogService() {
		return logService;
	}

	@Override
	@Transactional
//	@Transactional(noRollbackFor={IllegalArgumentException.class},timeout=5)
	public void transferSimple(String fromAccountNo, String toAccountNo, 
			BigDecimal amount) {
		transfer(toAccountNo, amount);
//		try {	//测试timeout属性
//			Thread.sleep(7*1000);
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
		transfer(fromAccountNo, amount.negate());
	}

	private void transfer(String accountNo, BigDecimal amount) {
		int affectCount = accountMapper.updateBalanceByAccountNo(accountNo, amount);
		Assert.isTrue(affectCount == 1, String.format("账户[%s]不存在或者余额不足", accountNo));
	}
	
	@Override
	public void transferSimpleNoTrans(String fromAccountNo, String toAccountNo, BigDecimal amount) {
		//case 本身无事务，调用本类、其他类实例方法也无事务
		transfer(toAccountNo, amount);
		transfer(fromAccountNo, amount.negate());
		
		//case 本身无事务，调用本类实例方法方法有事务
//		transferSimple(fromAccountNo, toAccountNo, amount);
	}
	
	@Override
	@Transactional
	public void transfer(String fromAccountNo, String toAccountNo, BigDecimal amount) {
		//step 1
		Account toAccount = accountMapper.getByAccountNo(toAccountNo);
		toAccount.setBalance(toAccount.getBalance().add(amount));
		int affectCount = accountMapper.updateBalanceById(toAccount);
		Assert.isTrue(affectCount == 1, "账户余额表更新转入账户余额失败");
		//step 2
		logService.addLog("转账更新转入账户", "ACCOUNT", "1234567890", toAccount);
		//step 3
		Account fromAccount = accountMapper.getByAccountNo(fromAccountNo);
		fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
		affectCount = accountMapper.updateBalanceById(fromAccount);
		Assert.isTrue(affectCount == 1, "账户余额表更新转出账户余额失败");
		
		//step 4
		try {
			logService.addLog("转账更新转出账户", "ACCOUNT", "1234567890123456789012345678901234567890", fromAccount);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public int updateBalanceByAccountNo(String accountNo, BigDecimal amount) {
		return accountMapper.updateBalanceByAccountNo(accountNo, amount);
	}
	
	
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	@Override
	public SystemLog addLog(String topic, String tableName, Object old, Object now) {
		SystemLog log = new SystemLog();
		log.setTopic(StringUtils.isBlank(topic)?"":topic);
		log.setTableName(StringUtils.isBlank(tableName)?"":tableName);
		try {
			log.setJsonPrev(Objects.equals(old, null) ? "" : objectMapper.writeValueAsString(old));
			log.setJson(Objects.equals(now, null) ? "" : objectMapper.writeValueAsString(now));
		} catch (JsonProcessingException e) {
			logger.info(e.getMessage(),e);
		}
		log.setLastModifiedTime(new Date());
		logMapper.insert(log);
		return log;
	}
	
	@Override
//	@Transactional	//case1 测试抛检查异常是否回滚;测试抛运行时异常是否回滚
//	@Transactional(noRollbackFor={IllegalArgumentException.class})
	@Transactional(rollbackFor = {DbException.class})
	public void transferException(String fromAccountNo, String toAccountNo, 
			BigDecimal amount) throws Exception{
		transfer(toAccountNo, amount);
		try {
			transfer(fromAccountNo, amount.negate());
		} catch (Exception e) {
//			throw e;	//如果e是运行时异常回滚
			
//			if (e instanceof IllegalArgumentException) {
//				throw (IllegalArgumentException)e;
//			}
			
			throw new DbException();
		}
	}	
}
